package com.springmvc.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.RequestParam;
import com.springmvc.annotation.ResponseBoby;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.exception.ContextException;
import com.springmvc.handler.MyHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * SpringMvc核心控制器
 */
public class DispatcherServlet extends HttpServlet {

    private WebApplicationContext webApplicationContext;

    // 存储URL和对象的方法映射关系
    List<MyHandler> handlerList = new ArrayList<MyHandler>();

    @Override
    public void init() throws ServletException {
        // 1.Servlet初始化的时候，读取初始化的参数 classpath:springmvc.xml
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
        // 2.创建Spring容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);
        // 3.初始化Spring容器
        webApplicationContext.refresh();
        // 4、初始化请求映射 /user/query  ----> Controller ----> method ----> paramter
        initHandleMapping();
        System.out.println("请求地址和控制器方法映射关系"+handlerList);
    }

    /**
     * 初始化请求映射
     */
    private void initHandleMapping() {
        // 判断IOCmap中是否有bean对象
        if (webApplicationContext.iocMap.isEmpty()){
            throw new ContextException("Spring容器为空");
        }
        for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(Controller.class)){
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                   if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
                       RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
                       // /user/query
                       String url = requestMappingAnnotation.value();
                       MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
                       handlerList.add(handler);
                   }
                }
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 请求的分发处理
        executeDispatch(req,resp);
    }

    // 请求分发 处理
    private void executeDispatch(HttpServletRequest req, HttpServletResponse resp) {
        MyHandler handler = getHandler(req);
        try {
            if (handler == null){
                resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
            }else {
                Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
                // 定义一个参数的数组
                Object[] params = new Object[parameterTypes.length];
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
                        params[i] = req;
                    }else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
                        params[i] = resp;
                    }
                }
                // 获取请求中的参数集合
                Map<String, String[]> parameterMap = req.getParameterMap();
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    String name = entry.getKey();
                    Object value = entry.getValue()[0];
                    System.out.println("请求参数："+name+"-----"+value);
                    int index = hasRequestParam(handler.getMethod(), name);
                    if (index != -1) {
                        params[index] = value;
                    }else {
                        List<String> names = getParameterName(handler.getMethod());
                        System.out.println(names);
                    }
                    // 后期有待优化
                    params[2] = value;
                }

                // 调用控制中的方法
                Object result = handler.getMethod().invoke(handler.getController(), parameterTypes);
                if (result instanceof String){
                    // 跳转到jsp
                    String viewName = (String) result;
                    if (viewName.contains(":")){
                        // "forward:/user.jsp"
                        String viewType = viewName.split(":")[0];
                        String viewPage = viewName.split(":")[1];
                        if (viewType.equals("")){
                            req.getRequestDispatcher(viewPage).forward(req,resp);
                        }else {
                            resp.sendRedirect(viewPage);
                        }
                    }else {
                        // 默认就转发
                        req.getRequestDispatcher(viewName).forward(req,resp);
                    }
                }else {
                    // 返回JSON数据
                    Method method = handler.getMethod();
                    if (method.isAnnotationPresent(ResponseBoby.class)){
                        // 把返回值调用JSON转换工具转为JSON字符串
                        ObjectMapper mapper = new ObjectMapper();
                        String json = mapper.writeValueAsString(result);
                        resp.setContentType("text/html;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        out.print(json);
                        out.flush();
                        out.close();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据请求地址获取Handler对象
     * @param req
     * @return
     */
    public MyHandler getHandler(HttpServletRequest req) {
        // /user/query
        String requestURI = req.getRequestURI();
        for (MyHandler myHandler : handlerList) {
            if (myHandler.getUrl().equals(requestURI)) {
                return myHandler;
            }
        }
        return null;
    }

    /**
     * 判断控制器方法的参数是否有requestParam注解，且找到对应value值 如果找到 返回
     * @param method
     * @param name
     * @return
     */
    public int hasRequestParam(Method method,String name) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter p = parameters[i];
            boolean b = p.isAnnotationPresent(RequestParam.class);
            if (b) {
                RequestParam requestParam = p.getAnnotation(RequestParam.class);
                String requestParamValue = requestParam.value();
                if (name.equals(requestParamValue)) {
                    return i;
                }
            }
        }
        return -1;
    }

    // 反射控制器方法名
    public List<String> getParameterName(Method method){
        List<String> list = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            list.add(parameter.getName());
        }
        return list;
    }

}
